package com.wsh.redis.redisdemo.util;

import com.wsh.redis.redisdemo.exception.SelfException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author wsh
 * @date 2021/9/15 20:28
 */
@Slf4j
public class RedisUtil {

    @SuppressWarnings("unchecked")
    private RedisTemplate<String, Object> redisTemplate =
            ApplicationContextUtil.getBean(RedisTemplate.class);


    // =================================== 功能性命令 ============================================

    /**
     * 判断是否有key
     *
     * @param key 键
     * @return Boolean
     */
    public static Boolean exists(String key) {
        Boolean aBoolean = false;
        try {
            aBoolean = new RedisUtil().redisTemplate.hasKey(key);
        } catch (Exception e) {
            log.error("错误：{}", e.getMessage());
            e.printStackTrace();
        }
        return aBoolean;
    }

    /**
     * 获取数据类型
     *
     * @param key 键
     * @return 类型
     */
    public static String type(String key) {
        DataType type = new RedisUtil().redisTemplate.type(key);
        assert type != null;
        return type.code();
    }

    /**
     * 指定缓存时间
     *
     * @param key  键
     * @param time 缓存时间（单位：秒）
     * @return Boolean
     */
    public static Boolean expire(String key, long time) {
        if (time <= 0) {
            time = 1;
        }
        return new RedisUtil().redisTemplate.expire(key, time, TimeUnit.SECONDS);
    }

    /**
     * 获取过期时间
     *
     * @param key 键
     * @return 时间
     */
    public static Long getExpire(String key) {
        return new RedisUtil().redisTemplate.getExpire(key);
    }

    /**
     * 判断key是否存在
     *
     * @param key 键
     * @return Boolean
     */
    public static Boolean hasKey(String key) {
        try {
            return new RedisUtil().redisTemplate.hasKey(key);
        } catch (Exception e) {
            log.error("判断key是否存在失败");
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除key
     *
     * @param key 键
     * @return 数量
     */
    public static Long delete(String... key) {
        if (key.length < 1 || ObjectUtils.isEmpty(key)) {
            return 0L;
        }
        return new RedisUtil().redisTemplate.delete(Arrays.asList(key));
    }

    /**
     * 获取所有key
     *
     * @return keys
     */
    public static Set<String> keys() {
        return new RedisUtil().redisTemplate.keys("*");
    }

    /**
     * flushDb
     */
    public static void flushDb() {
        new RedisUtil().redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
                redisConnection.flushDb();
                return "ok";
            }
        });
    }

    // =================================== String ============================================

    /**
     * 获取缓存
     *
     * @param key
     * @return
     */
    public static Object get(String key) {
        if (StringUtils.isNotEmpty(key)) {
            return new RedisUtil().redisTemplate.opsForValue().get(key);
        }
        return null;
    }

    /**
     * 普通缓存存储
     *
     * @param key   键
     * @param value 值
     * @return boolean
     */
    public static boolean set(String key, Object value) {
        try {
            new RedisUtil().redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 存入缓存，设置过期时间
     *
     * @param key   键
     * @param value 值
     * @param time  过期时间
     * @return boolean
     */
    public static boolean set(String key, String value, long time) {
        try {
            new RedisUtil().redisTemplate.opsForValue()
                    .set(key, value, time, TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 递增
     *
     * @param key   键
     * @param delta 递增因子
     * @return long
     */
    public static Long incr(String key, long delta) {
        if (delta < 0) {
            throw new SelfException("递增因子大于0");
        }
        return new RedisUtil().redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 递减
     *
     * @param key   键
     * @param delta 减少的值
     * @return long
     */
    public static Long decr(String key, long delta) {
        if (delta < 0) {
            throw new SelfException("递增因子大于0");
        }
        return new RedisUtil().redisTemplate.opsForValue().decrement(key, delta);
    }

    /**
     * 追加字符串
     *
     * @param key   键
     * @param value 追加的字符串
     * @return integer
     */
    public static Integer append(String key, String value) {
        return new RedisUtil().redisTemplate.opsForValue().append(key, value);
    }

    /**
     * 获取key的二进制位
     *
     * @param key   键
     * @param value 位数
     * @return boolean
     */
    public static Boolean getBit(String key, long value) {
        return new RedisUtil().redisTemplate.opsForValue().getBit(key, value);
    }


    /**
     * 将字符串看作是二进制位串，并将位串中偏移量为OFFSET的二进制位设置位VALUE
     *
     * @param key  key
     * @param v1   值
     * @param flag
     * @return boolean
     */
    public static Boolean setBit(String key, long v1, boolean flag) {
        return new RedisUtil().redisTemplate.opsForValue().setBit(key, v1, flag);
    }

    // =================================== map 散列 ============================================

    /**
     * hGet 散列里面联合起来
     *
     * @param key
     * @param item
     * @return
     */
    public static Object hGet(String key, String item) {
        return new RedisUtil().redisTemplate.opsForHash().get(key, item);
    }

    /**
     * hSet 添加
     *
     * @param key
     * @param hk
     * @param hv
     */
    public static void hSet(String key, Object hk, Object hv) {
        new RedisUtil().redisTemplate.opsForHash().put(key, hk, hv);
    }

    /**
     * hmset 添加多个散列
     *
     * @param key 键
     * @param map 值
     */
    public static void hmSet(String key, Map<Object, Object> map) {
        new RedisUtil().redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 获取key对应所有对应的集合
     *
     * @param key
     * @return
     */
    public static List<Object> hmGet(String key) {
        return new RedisUtil().redisTemplate.opsForHash().values(key);
    }

    /**
     * 获取当前key对应的长度
     *
     * @param key 键
     * @return 长度
     */
    public static Long hLen(String key) {
        return new RedisUtil().redisTemplate.opsForHash().size(key);
    }

    /**
     * 删除has key  hDel
     *
     * @param key  键
     * @param keys hash key
     * @return 删除的数量
     */
    public static Long hDel(String key, List<Object> keys) {
        return new RedisUtil().redisTemplate.opsForHash().delete(key, keys);
    }

    /**
     * hExists key filed 检查给定的键是否在散列中
     *
     * @param key   键
     * @param field 域
     * @return Boolean
     */
    public static Boolean hExists(String key, Object field) {
        return new RedisUtil().redisTemplate.opsForHash().hasKey(key, field);
    }

    /**
     * hKeys key-name 获取散列中包含的所有键
     *
     * @param key 键
     * @return key集合
     */
    public static Set<Object> hKeys(String key) {
        return new RedisUtil().redisTemplate.opsForHash().keys(key);
    }

    /**
     * HVALS ---> HVALS key-name 获取散列中所有的值
     *
     * @param key 键
     * @return 域
     */
    public static List<Object> hValS(String key) {
        return new RedisUtil().redisTemplate.opsForHash().values(key);
    }

    /**
     * HGETALL ---> HGETALL key-name 获取散列包含的所有键值对
     *
     * @param key 键
     * @return Map
     */
    public static Map<Object, Object> hGetAll(String key) {
        return new RedisUtil().redisTemplate.opsForHash().entries(key);
    }

    /**
     * HINCRBY ---> HINCRBY key-name key increment 将键key存储的值加上整数increment
     *
     * @param key       key
     * @param field     域
     * @param increment 增长值
     * @return Long
     */
    public static Long hIncrBy(String key, Object field, Long increment) {
        return new RedisUtil().redisTemplate.opsForHash().increment(key, field, increment);
    }

    /**
     * HINCRBYFLOAT ---> HINCRBYFLOAT key-name key increment 将键存储的值加上浮点数increment
     *
     * @param key       key
     * @param field     域
     * @param increment 增长值
     * @return Double
     */
    public static Double hIncrByFloat(String key, Object field, Float increment) {
        return new RedisUtil().redisTemplate.opsForHash().increment(key, field, increment);
    }

    // =================================== zSet 有序集合 ============================================

    /**
     * ZADD ---> ZADD key-name score member [score member ...] 将带有给定分值的成员添加到有序的集合里面
     *
     * @param key   键
     * @param field 域
     * @param score 分值
     */
    public static Boolean zAdd(String key, Object field, Integer score) {
        return new RedisUtil().redisTemplate.opsForZSet().add(key, field, score);
    }

    /**
     * ZREM ---> ZREM key-name member [member ...] 从有序集合里面移除给定的成员，并返回被移除的成员数量
     *
     * @param key   键
     * @param field 域
     * @return Long
     */
    public static Long zRem(String key, Object... field) {
        return new RedisUtil().redisTemplate.opsForZSet().remove(key, field);
    }

    /**
     * ZINCRBY ---> ZINCRBY key-name increment member 将member成员
     *
     * @param key
     * @param score
     * @param field
     * @return
     */
    public static Double zIncrBy(String key, Object score, Double field) {
        return new RedisUtil().redisTemplate.opsForZSet().incrementScore(key, score, field);
    }

    /**
     * ZCOUNT key-name min max 返回分值介于min和max之间的成员数量
     *
     * @param key
     * @param incrementStart
     * @param incrementEnd
     * @return
     */
    public static Long zCount(String key, Double incrementStart, Double incrementEnd) {
        return new RedisUtil().redisTemplate.opsForZSet().count(key, incrementStart, incrementEnd);
    }

    /**
     * ZRANK key-name member 返回成员member在有序集合中的排名
     *
     * @param key   键
     * @param field 域
     * @return Long
     */
    public static Long zRank(String key, Object field) {
        return new RedisUtil().redisTemplate.opsForZSet().rank(key, field);
    }

    /**
     * ZSCORE ---> ZSCORE key-name member 返回成员member的分值
     *
     * @param key
     * @param field
     * @return Double
     */
    public static Double zScore(String key, Object field) {
        return new RedisUtil().redisTemplate.opsForZSet().score(key, field);
    }

    /**
     * ZRANGE ---> ZRANGE key-name start end [withscores] 返回有序集合排名介于start和stop之间的成员，
     * 可选定withscores选项，那么命令会将承运的分值也一并返回
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public static Set<Object> zRange(String key, Long start, Long end) {
        return new RedisUtil().redisTemplate.opsForZSet().range(key, start, end);
    }

    /**
     * ZRANGEBYSCORE ---> ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
     * 获取有序集合中，分值介于min和max之间的所有成员
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public static Set<Object> zRange(String key, int min, int max) {
        return new RedisUtil().redisTemplate.opsForZSet().rangeByScore(key, min, max);
    }

    /**
     * ZREVRANK ---> ZREVRANK key-name member 返回有序集合里成员member的排名，成员按照分值从大到小排列
     *
     * @param key
     * @param field
     * @return
     */
    public static Long zRevRank(String key, Object field) {
        return new RedisUtil().redisTemplate.opsForZSet().reverseRank(key, field);
    }

    /**
     * ZREVRANGE ---> ZREVRANGE key-name 0 -1 [withscores] 返回有序集合给定排名范围的成员，成员按照分值从大到小排列
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public static Set<Object> zRevenge(String key, Long min, Long max) {
        return new RedisUtil().redisTemplate.opsForZSet().reverseRange(key, min, max);
    }

    /**
     * ZREMRANGEBYSCORE ---> ZREMRANGEBYSCORE key-name min max 移除有序集合中分值介于min和max之间所有的成员
     *
     * @param key
     * @param v1
     * @param v2
     */
    public static void zRemRangeByRank(String key, Double v1, Double v2) {
        new RedisUtil().redisTemplate.opsForZSet().removeRangeByScore(key, v1, v2);
    }

    /**
     * ZINTERSTORE ---> ZINTERSTORE dest-key key-count key [key ...] [WEIGHT weight [weight ...]]
     * 对给定的有序集合执行类似于集合的交集运算
     *
     * @param key
     * @param k1
     * @param k2
     */
    public static Long zInterStore(String key, String k1, String k2) {
        return new RedisUtil().redisTemplate.opsForZSet().intersectAndStore(key, k1, k2);
    }

    public static Long zInterStore(String key, Collection<String> list, String k) {
        return new RedisUtil().redisTemplate.opsForZSet().intersectAndStore(key, list, k);
    }

    /**
     * ZUNIONSTORE ---> ZUNIONSTORE dest-key key-count key [key ...] [WEIGHTS weight [weight ...]]
     * [[AGGREGATE SUM|MIN|MAX]] 对给定的有序集合执行类似于集合的并集运算
     *
     * @param key
     * @param k1
     * @param k2
     * @return
     */
    public static Long zUnionStore(String key, String k1, String k2) {
        return new RedisUtil().redisTemplate.opsForZSet().unionAndStore(key, k1, k2);
    }

    public static Long zUnionStore(String key, Collection<String> k, String k1) {
        return new RedisUtil().redisTemplate.opsForZSet().unionAndStore(key, k, k1);
    }

    // ============================================= list 列表====================================================

    /**
     * RPUSH ---> RPUSH KEY VALUE [VALUE ...] 将一个或多个值推入列表的右端
     *
     * @param key
     * @param value
     * @return
     */
    public static Long list(String key, Object... value) {
        return new RedisUtil().redisTemplate.opsForList().rightPush(key, value);
    }


}
